//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<XCtrls.cpp>
///	@path	~/src/gui/xgui/
///	@date	2008/05/30
///	@desc	.

#define _FULLAPI
#include "config/config.h"

#include "lib/system/sysinfo.h"

#include "gui/IGuiForm.h"
#include "gui/xgui/XCtrls.h"
#include "gui/composite/composition.h"
#include "gui/image/ImageResource.h"

namespace xeres {

	IMPL_IOBJECT_CLASS_DYNAMIC( XHolder );

	// XHolder
	XHolder::XHolder( void )
		: IGuiControl( Rect<int>() , true , true , false )
		, m_lastPos( -1 , -1 )
	{
	}
	// XHolder
	XHolder::XHolder( const Rect<int>& placement )
		: IGuiControl( placement , true , true , false )
		, m_lastPos( -1 , -1 )
	{
	}
	// ~XHolder
	XHolder::~XHolder( void )
	{

	}
	// OnMouseInput
	HandleInput XHolder::OnMouseInput( Updater& updater )
	{
		const Input& input = updater.GetInput();
		HandleInput result = INPUT_UNHANDLED;

		if( input.GetAction() == Input::ACTION_MOUSE_LB_CLICK && GetPlacement().is_inside( updater.GetPoint() ) )
		{
			m_clicked = true;
			m_lastPos = updater.GetMouseInput().GetPoint();
			RefWeak<IGuiForm> parent = GetParent();
			parent->RequestFocus( this , updater );
			parent->SetCapture( this );
			result = INPUT_HANDLED;
		}
		else if( m_clicked && input.GetAction() == Input::ACTION_MOUSE_MOVE )
		{
			Pair<int> cur = updater.GetMouseInput().GetPoint();
			if( !GetFocus() )
			{
				m_clicked = false;
				UpdateControl();
			}
			else if( cur != m_lastPos )
			{
				Pair<int> diff = cur - m_lastPos;
				m_lastPos = cur;
				RefWeak<IGuiForm> parent = GetParent();
				Pair<int> pos = parent->GetPosition() + diff;
				parent->SetPosition( pos );
				result = INPUT_HANDLED;
			}
		}
		else if( input.GetAction() == Input::ACTION_MOUSE_LB_UP )
		{
			if( m_clicked )
			{
				Pair<int> cur = updater.GetMouseInput().GetPoint();
				Pair<int> diff = cur - m_lastPos;
				RefWeak<IGuiForm> parent = GetParent();
				Pair<int> pos = parent->GetPosition() + diff;
				parent->SetPosition( pos );
				parent->ReleaseCapture();
				result = INPUT_HANDLED;
			}
			m_clicked = false;
			if( GetParent()->GetFocusing() ==  this )
				GetParent()->ClearFocus( updater );
		}
		return result;
	}
	// CanHaveFocus
	bool XHolder::CanHaveFocus( void ) const
	{
		return false;
	}
		
	IMPL_IOBJECT_CLASS_DYNAMIC( XTitle );

	// XTitle
	XTitle::XTitle( void )
		: XHolder()
		, GuiSyleComp( new CGuiStyle )
	{
		m_title.SetStyle( Ref<CTextStyle>( m_guiStyle ) );
	}
	// XTitle
	XTitle::XTitle( const WString& text , const Rect<int>& placement )
		: XHolder( placement )
		, GuiSyleComp( new CGuiStyle )
		, m_title( text )
	{
		m_title.SetStyle( Ref<CTextStyle>( m_guiStyle ) );
	}
	// ~XTitle
	XTitle::~XTitle( void )
	{

	}
	// Draw
	void XTitle::Draw( Composition& composition )
	{
		Rect<int> rect( GetBound() );
		if( m_hover )
		{
			Gdiplus::SolidBrush brush( Gdiplus::Color(133,110,110,110) );
			composition.graphics().FillRectangle( &brush , toRect(rect) );
		}
		else
		{
			Gdiplus::SolidBrush brush( Gdiplus::Color(133,33,33,33) );
			composition.graphics().FillRectangle( &brush , toRect(rect) );
		}
		composition.graphics().DrawRectangle( &Gdiplus::Pen( Gdiplus::Color(255,0,0,0) ) , toRect(GetRect()) );

		Rect<int> rt( GetRect() );
		rt.extend( Pair<int>( -6 , -2 ) );

		m_title.DrawText( composition , rt , rect );
	}
	// SetText
	void XTitle::SetText( const WString& text )
	{
		m_title.SetString( text );
		UpdateControl();
	}
	// GetStyle
	RefWeak<CGuiStyle> XTitle::GetStyle( void )
	{
		return m_guiStyle;
	}
	// OnKeyInput
	HandleInput XTitle::OnKeyInput( Updater& updater )
	{
		HandleInput result = __super::OnKeyInput( updater );
		if( result == INPUT_HANDLED )
			ChangeStyle();
		return result;
	}
	// OnMouseInput
	HandleInput XTitle::OnMouseInput( Updater& updater )
	{
		HandleInput result = __super::OnMouseInput( updater );
		if( result == INPUT_HANDLED )
			ChangeStyle();
		return result;
	}
	// OnEnable
	void XTitle::OnEnable( bool enable )
	{
		__super::OnEnable( enable );
		ChangeStyle();
	}
	// OnFocus
	void XTitle::OnFocus( bool focusing )
	{
		__super::OnEnable( focusing );
		ChangeStyle();
	}
	// OnMouseOver
	void XTitle::OnMouseOver( bool hovering )
	{
		__super::OnMouseOver( hovering );
		ChangeStyle();
	}
	// ChangeStyle
	void XTitle::ChangeStyle( void )
	{
		ResetStyle( m_enable , m_clicked , m_hover , m_focus );
		m_title.SetStyle( Ref<CTextStyle>( m_activeGuiStyle ) );
	}

	IMPL_IOBJECT_INTERFACE( IScrollBar );

	// IScrollBar
	IScrollBar::IScrollBar( void )
		: IGuiControl( Rect<int>() , true , true , true )
		, m_scrollRange( 100 )
		, m_scrollPos( 0 )
		, m_lastPos( -1 , -1 )
		, m_inBar( false )
		, m_scrolling( false )
	{
	}
	// ~IScrollBar
	IScrollBar::~IScrollBar( void )
	{
	}
	// GetScrollRange
	int IScrollBar::GetScrollRange( void ) const
	{
		return m_scrollRange;
	}
	// SetScrollRange
	void IScrollBar::SetScrollRange( int range )
	{
		m_scrollRange = range;
		m_scrollPos = clamp<int>( m_scrollPos , 0 , m_scrollRange );
		OnSetScrollRange();
		UpdateControl();
	}
	// GetScrollPos
	int IScrollBar::GetScrollPos( void ) const
	{
		return m_scrollPos;
	}
	// SetScrollPos
	void IScrollBar::SetScrollPos( int pos )
	{
		m_scrollPos = clamp<int>( pos , 0 , m_scrollRange );
		OnSetScrollPos();
		UpdateControl();
	}
	// OnKeyInput
	HandleInput IScrollBar::OnKeyInput( Updater& updater )
	{
		if( !GetEnable() || !GetVisible() )
			return INPUT_UNHANDLED;

		const KeyInput& input = updater.GetKeyInput();
		if( input.IsPressed() )
		{
			// Only process home & end 2 messages.
			if( input.GetKey() == KeyInput::KEY_HOME )
			{
				SetScrollPos( 0 );
				return INPUT_HANDLED;
			}
			else if( input.GetKey() == KeyInput::KEY_END )
			{
				SetScrollPos( m_scrollRange );
				return INPUT_HANDLED;
			}
		}
		return INPUT_UNHANDLED;
	}
	// OnMouseInput
	HandleInput IScrollBar::OnMouseInput( Updater& updater )
	{
		if( !GetEnable() || !GetVisible() )
			return INPUT_UNHANDLED;

		Pair<int> point = updater.GetPoint();

		if( !m_scrolling && !HitTest( point ) )
			return INPUT_UNHANDLED;

		switch( updater.GetInput().GetAction() )
		{
		case Input::ACTION_MOUSE_LB_CLICK:
			if( HitTest(point) )
			{
				m_clicked = true;
				if( HitTestBar( point ) )
				{
					m_scrolling = true;
					m_lastPos = point;
					m_inBar = true;
				}
				else
				{
					OnClickBlank( point );
				}
				RefWeak<IGuiForm> parent = GetParent();
				parent->RequestFocus( this );
				parent->SetCapture( this );
				return INPUT_HANDLED;
			}
			return INPUT_UNHANDLED;
		case Input::ACTION_MOUSE_LB_UP:
			{
				m_clicked = false;
				if( m_scrolling )
				{
					m_scrolling = false;
				}
				RefWeak<IGuiForm> parent = GetParent();
				parent->ReleaseCapture();
				return INPUT_HANDLED;
			}
		case Input::ACTION_MOUSE_MOVE:
			{
				if( m_clicked && m_scrolling )
				{
					m_inBar = HitTestBar( point );
					Pair<int> diff = point - m_lastPos;
					m_lastPos = point;
					int v = OnDragBar( diff );
					if( v != 0 )
					{
						SetScrollPos( v + m_scrollPos );
						UpdateControl();
					}
					return INPUT_HANDLED;
				}
				else
				{
					bool in_bar = HitTestBar( point );
					if( in_bar != m_inBar )
					{
						m_inBar = in_bar;
						UpdateControl();
					}
					return INPUT_UNHANDLED;
				}
			}
		}
		return INPUT_UNHANDLED;
	}

	IMPL_IOBJECT_INTERFACE( IScrollBarVertical );

	// IScrollBarVertical
	IScrollBarVertical::IScrollBarVertical( void )
	{
	}
	// OnDragBar
	int IScrollBarVertical::OnDragBar( const Pair<int>& diff )
	{
		return diff.y;
	}

	IMPL_IOBJECT_INTERFACE( IScrollBarHorizontal );

	// IScrollBarHorizontal
	IScrollBarHorizontal::IScrollBarHorizontal( void )
	{

	}
	// OnDragBar
	int IScrollBarHorizontal::OnDragBar( const Pair<int>& diff )
	{
		return diff.x;
	}

	IMPL_IOBJECT_CLASS_DYNAMIC( XScrollBarVertical );

	// XScrollBarVertical
	XScrollBarVertical::XScrollBarVertical( void )
		: m_headReserve(0)
		, m_tailReserve(0)
		, m_pos(0)
		, m_range(100)
		, m_page(10)
	{
	}
	// ~XScrollBarVertical
	XScrollBarVertical::~XScrollBarVertical( void )
	{
	}
	// OnClose
	void XScrollBarVertical::OnClose( void )
	{
		m_onScrollCall = NULL;
	}
	// OnSize
	void XScrollBarVertical::OnSize( void )
	{
		Rect<int> rect( GetRect() );
		m_scrollRegion.set( rect.left() , rect.top() + m_headReserve , rect.right() , rect.bottom() - m_tailReserve );
		float bar = m_page / ( m_range + m_page );
		int barsize = int( m_scrollRegion.height() * bar );

		SetScrollRange( m_scrollRegion.height() - barsize );

 		m_scrollBarRect.set(
 			m_scrollRegion.left() ,
 			m_scrollRegion.top() + GetScrollPos() ,
 			m_scrollRegion.right() , 
			m_scrollRegion.top() + GetScrollPos() + barsize );
	}
	// HitTestBar
	bool XScrollBarVertical::HitTestBar( const Pair<int>& pt )
	{
		Rect<int> rect( m_scrollBarRect );
		rect.offset( GetPosition() );
		return rect.is_inside( pt );
	}
	// GetScrollLength
	int XScrollBarVertical::GetScrollLength( void )
	{
		return m_scrollRegion.height();
	}
	// OnDragBar
	int XScrollBarVertical::OnDragBar( const Pair<int>& diff )
	{
		return __super::OnDragBar( diff );
	}
	// OnClickBlank
	int XScrollBarVertical::OnClickBlank( const Pair<int>& pos )
	{
		return 0;
	}
	// OnSetScrollRange
	void XScrollBarVertical::OnSetScrollRange( void )
	{
		//OnSize();
		m_pos = float( GetScrollPos() ) / float( GetScrollRange() ) * m_range;
		if( m_onScrollCall )
			m_onScrollCall( m_pos );
	}
	// OnSetScrollPos
	void XScrollBarVertical::OnSetScrollPos( void )
	{
		OnSize();
		
	}
	// Tick
	void XScrollBarVertical::Tick( Updater& updater , float dTime )
	{
	}
	// Draw
	void XScrollBarVertical::Draw( Composition& composition )
	{
		Rect<int> rect( GetRect() );

		if( m_hover )
		{
			Gdiplus::SolidBrush brush( Gdiplus::Color(33,33,33) );
			composition.graphics().FillRectangle( &brush , toRect(rect) );
		}
		else
		{
			Gdiplus::SolidBrush brush( Gdiplus::Color(110,110,110) );
			composition.graphics().FillRectangle( &brush , toRect(rect) );
		}

		if( m_hover && m_inBar )
		{
			Gdiplus::SolidBrush brush( Gdiplus::Color(60,60,110) );
			composition.graphics().FillRectangle( &brush , toRect( m_scrollBarRect ) );
		}
		else
		{
			Gdiplus::SolidBrush brush( Gdiplus::Color(60,60,60) );
			composition.graphics().FillRectangle( &brush , toRect( m_scrollBarRect ) );
		}
	}

	// SetRange
	void XScrollBarVertical::SetRange( float range , float page )
	{
		m_range = range;
		m_page = page;
		m_pos = min<float>( m_pos , m_range );
		OnSize();
	}

	// SetPos
	void XScrollBarVertical::SetPos( float pos )
	{
		m_pos = pos;
		OnSize();
	}

	IMPL_IOBJECT_CLASS_DYNAMIC( XScrollBarHorizontal );

	// XScrollBarHorizontal
	XScrollBarHorizontal::XScrollBarHorizontal( void )
		: m_headReserve(0)
		, m_tailReserve(0)
		, m_pos(0)
		, m_range(100)
		, m_page(10)
	{
	}
	// ~XScrollBarHorizontal
	XScrollBarHorizontal::~XScrollBarHorizontal( void )
	{
	}
	// OnClose
	void XScrollBarHorizontal::OnClose( void )
	{
		m_onScrollCall = NULL;
	}
	// OnSize
	void XScrollBarHorizontal::OnSize( void )
	{
		Rect<int> rect( GetRect() );
		m_scrollRegion.set( rect.left() + m_headReserve , rect.top()  , rect.right() - m_tailReserve , rect.bottom()  );
		float bar = m_page / ( m_range + m_page );
		int barsize = int( m_scrollRegion.width() * bar );

		SetScrollRange( m_scrollRegion.width() - barsize );

 		m_scrollBarRect.set(
 			m_scrollRegion.left() + GetScrollPos() ,
 			m_scrollRegion.top()  ,
 			m_scrollRegion.left() + GetScrollPos() + barsize , 
			m_scrollRegion.bottom() );
	}
	// HitTestBar
	bool XScrollBarHorizontal::HitTestBar( const Pair<int>& pt )
	{
		Rect<int> rect( m_scrollBarRect );
		rect.offset( GetPosition() );
		return rect.is_inside( pt );
	}
	// GetScrollLength
	int XScrollBarHorizontal::GetScrollLength( void )
	{
		return m_scrollRegion.height();
	}
	// OnDragBar
	int XScrollBarHorizontal::OnDragBar( const Pair<int>& diff )
	{
		return __super::OnDragBar( diff );
	}
	// OnClickBlank
	int XScrollBarHorizontal::OnClickBlank( const Pair<int>& pos )
	{
		return 0;
	}
	// OnSetScrollRange
	void XScrollBarHorizontal::OnSetScrollRange( void )
	{
		//OnSize();
		m_pos = float( GetScrollPos() ) / float( GetScrollRange() ) * m_range;
		if( m_onScrollCall )
			m_onScrollCall( m_pos );
	}
	// OnSetScrollPos
	void XScrollBarHorizontal::OnSetScrollPos( void )
	{
		OnSize();
		
	}
	// Tick
	void XScrollBarHorizontal::Tick( Updater& updater , float dTime )
	{
	}
	// Draw
	void XScrollBarHorizontal::Draw( Composition& composition )
	{
		Rect<int> rect( GetRect() );

		if( m_hover )
		{
			TRACE_INFO( _S("hover") );
			Gdiplus::SolidBrush brush( Gdiplus::Color(33,33,33) );
			composition.graphics().FillRectangle( &brush , toRect(rect) );
		}
		else
		{
			Gdiplus::SolidBrush brush( Gdiplus::Color(110,110,110) );
			composition.graphics().FillRectangle( &brush , toRect(rect) );
		}

		if( m_hover && m_inBar )
		{
			Gdiplus::SolidBrush brush( Gdiplus::Color(60,60,110) );
			composition.graphics().FillRectangle( &brush , toRect( m_scrollBarRect ) );
		}
		else
		{
			Gdiplus::SolidBrush brush( Gdiplus::Color(60,60,60) );
			composition.graphics().FillRectangle( &brush , toRect( m_scrollBarRect ) );
		}
	}

	// SetRange
	void XScrollBarHorizontal::SetRange( float range , float page )
	{
		m_range = range;
		m_page = page;
		m_pos = min<float>( m_pos , m_range );
		OnSize();
	}

	// SetPos
	void XScrollBarHorizontal::SetPos( float pos )
	{
		m_pos = pos;
		OnSize();
	}
	
} // namespace xeres
